<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>9.对象的扩展——属性的可枚举性和遍历</title>
	</head>
	<body>
		<script type="text/javascript">
			//六、属性的可枚举性和遍历
			//1.可枚举性
			//对象的每个属性都有一个描述对象(Descriptor)，用来控制该属性的行为
			//Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
			let obj = {foo:123};
			console.log(Object.getOwnPropertyDescriptor(obj,'foo'))//{value: 123, writable: true, enumerable: true, configurable: true}
			//描述对象的enumerable属性，称为“可枚举性”，如果该属性为false，就表示某些操作会忽略当前属性。
			//目前，有四个操作会忽略enumerable为false的属性。
			//a.for...in 循环：只遍历对象自身的和继承的可枚举的属性。
			//b.Object.key()：返回对象自身的所有可枚举的属性的键名。
			//c.JSON.stringify()：只串行化对象自身的可枚举的属性。
			//d.Object.assign()：忽略enumerable为false的属性，只拷贝对象自身的可枚举的属性。
			//这四个操作之中，前三个是 ES5 就有的，最后一个Object.assign()是 ES6 新增的。其中，只有for...in会返回继承的属性，其他三个方法都会忽略继承的属性，只处理对象自身的属性。实际上，引入“可枚举”（enumerable）这个概念的最初目的，就是让某些属性可以规避掉for...in操作，不然所有内部属性和方法都会被遍历到。比如，对象原型的toString方法，以及数组的length属性，就通过“可枚举性”，从而避免被for...in遍历到。
			console.log(Object.getOwnPropertyDescriptor(Object.prototype,'toString').enumerable)  //false
			console.log(Object.getOwnPropertyDescriptor([],'length').enumerable)//false
			//上面代码中，toString和length属性的enumerable都是fasle,因此for...in不会遍历到这两个继承自原型的属性。
			//另外，ES6规定，所有Class的原型的方法都是不可枚举的。
			console.log(Object.getOwnPropertyDescriptor(class {foo(){}}.prototype,'foo').enumerable)  //false
			//总的来说，操作中引入继承的属性会让问题复杂化，大多数时候，我们只关心对象自身的属性。所以，尽量不要用for...in循环，而用Object.key()代替。
			
			//2、属性的遍历
			//ES6一共有5种方法可以遍历对象的属性
			
			//a.for...in
			//for...in循环遍历对象自身的和继承的可枚举属性（不含Symbol属性）
			
			//b.Object.key(obj)
//			Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol)的键名.

			//c. object.getOwnPropertyNames(obj)
			//Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包含不可枚举属性)的键名
			
			//d. object.getOwnPropertySymbols(obj)
			//Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性的键名.
			
			//e.Reflect.ownKeys(obj)
			//Reflect.ownkeys返回一个数组，包含对象自身的所有键名，不管键名是Symbol或字符串，也不管是否可枚举。
			//以上5种方法遍历对象的键名，都遵守同样的属性遍历的次序规则。
			//①、首先遍历所有数值键，按照数值升序排列
			//②、其次遍历所有字符串键，按照加入时间升序排列
			//③、最后遍历所有Symbol键，按照加入时间升序排列。
			console.log(Reflect.ownKeys({[Symbol()]:0,b:0,10:0,2:0,a:0}))//["2", "10", "b", "a", Symbol()]
			//上面代码中，Reflect.ownKeys方法返回一个数组，包含了参数对象的所有属性。这个数组的属性次序是这样的，首先是数值属性2和10，其次是字符串属性b和a,最后是Symbol属性。
			
			
		</script>
	</body>
</html>
